home *** CD-ROM | disk | FTP | other *** search
Text File | 1997-05-05 | 10.5 KB | 398 lines | [TEXT/ttxt] |
- // This may look like C code, but it is really -*- C++ -*-
- /*
- ************************************************************************
- *
- * Vocabulary service
- *
- * I/O operations and loading/saving of vocabularies and
- * vocabulary items
- *
- * Simple vocabulary entries are written as follows
- * X,name= value
- * where X is the one-letter item type: I for integer, S for string, D for
- * double and R for vocabulary reference. value is an ascii representation
- * for the item's value, in "%d" and "%g" printf-format for integer and
- * double items correspondingly, and "" or a file_name for a VocRef item.
- * String values are quoted (in double quotes) strings. A double-quote
- * sign within the string body is escaped with the backslash, and
- * backslash itself within the string body is doubled.
- *
- * Vocabulary is written as follows
- * Voc(X...X): name= "comment" (
- * vocabulary entry1
- * ......
- * vocabulary entryn
- * )
- *
- * where X is an option code (R for read-only, H for homegeneous)
- * comment is written as a quoted string
- *
- * $Id: voc_io.cc,v 1.3 1997/03/04 21:02:34 oleg Exp oleg $
- *
- ************************************************************************
- */
-
- #ifdef __GNUC__
- #pragma implementation
- #endif
-
-
- #include "myenv.h"
- #include <ctype.h>
- #include <std.h>
- #include <fstream.h>
-
- #include "voc.h"
-
- //------------------------------------------------------------------------
- // Service I/O functions
-
- // Write a string with the appropriate quoting
- static void write_quoted_string(ostream& outs, const char * str)
- {
- assert( outs.good() );
- outs << '"'; // Start with a quote
- for(register const char * p=str; *p; p++)
- if( *p == '"' )
- outs << "\\\"";
- else if( *p == '\\' )
- outs << "\\\\";
- else
- outs << *p;
- outs << '"'; // Finish with a quote
- assert( outs.good() );
- }
-
- // Read a character from the input stream
- // and compare it with one of the expected
- // characters (specified as a string)
- // If the read character was expected, return
- // its index in the expected char string
- // Otherwise, write a nasty message
- static int expect_char(istream& ins, const char * expected_chars,
- const char * comment)
- {
- assert( ins.good() );
- char c;
- assert( ins.get(c).good() );
- register char * p = strchr(expected_chars,c);
- if( p == 0 )
- _error("Wrong character %c (0x%x) %s, '%s' expected",
- c,c,comment,expected_chars);
- return p-expected_chars;
- }
- // Read a quoted string
- // Returns a ptr to a globally allocated
- // string. We can read strings as big as
- // they can be (as they have been written)
- static char * read_quoted_string(istream& ins)
- {
- assert( ins.good() );
- char c;
-
- expect_char(ins,"\"","starting a quoted string");
-
- const int quantum = 100;
- int allocated_size = 100;
- char * str = (char *)malloc(allocated_size); // Do initial allocation
- assert( str != 0 );
- register int i = 0; // Current position
-
- for(;;)
- {
- assert( ins.get(c).good() );
- if( c == '\"' )
- break; // Final quote is encountered
- if( c == '\\' )
- {
- ins >> c; // Read the escaped character
- assert( ins.good() );
- }
- str[i++] = c;
- if( i == allocated_size - 1 )
- assert( (str = (char *)realloc(str,allocated_size+=quantum)) != 0 );
- }
-
- assert( ins.good() );
- assert( i < allocated_size - 1 );
-
- str[i++] = '\0'; // Terminate the string
- if( allocated_size - i > 10 ) // Get rid of big slack if any
- assert( (str = (char *)realloc(str,i < 8 ? 8 : i)) != 0 );
-
- return str;
- }
-
- //------------------------------------------------------------------------
- // Writing Simple Vocabulary Items
-
- // Write the type and the name
- // (and leave dumping of the value to
- // derived classes)
- void GenericVocItem::write_down(ostream& outs) const
- {
- assert( val_type != Vocab ); // Voc needs a special treatment
- outs << ( val_type == Int ? 'I' : val_type == Double ? 'D' :
- val_type == String ? 'S' : val_type == VocRef ? 'R' : 'X' );
- outs << ',' << name << "= ";
- }
-
-
- void IntVocItem::write_down(ostream& outs) const
- {
- GenericVocItem::write_down(outs);
- outs << value << endl;
- }
-
- void DoubleVocItem::write_down(ostream& outs) const
- {
- GenericVocItem::write_down(outs);
- outs << value << endl;
- }
-
- void StrVocItem::write_down(ostream& outs) const
- {
- GenericVocItem::write_down(outs);
- write_quoted_string(outs,value);
- outs << endl;
- }
-
- void VocRefItem::write_down(ostream& outs) const
- {
- GenericVocItem::write_down(outs);
- write_quoted_string(outs,voc_file_name ? voc_file_name : "");
- outs << endl;
- }
-
- //------------------------------------------------------------------------
- // Reading Simple Vocabulary Items
-
- // Read in and parse val_type & name
- // and return an item of an appropriate type
- // That is, if IntVocItem is being read, an IntVocItem
- // is returned
- // The function is declared as static member
- // function: on the one hand, the method cannot be
- // applied to an object (because a (derived) object
- // is being constructed by this function). The function
- // could've been declared as friend, but it must be
- // protected to prevent anybody from constructing
- // dangling items (not in any dictionary)
- GenericVocItem * GenericVocItem::read_in(istream& ins)
- {
- assert( ins.good() );
- int c = ins.peek(); // Get a type character and analyze it
- Type type = (Type)(-1); // Initialize with some insane value
-
- switch(c)
- {
- case 'I':
- type = Int;
- break;
-
- case 'D':
- type = Double;
- break;
-
- case 'S':
- type = String;
- break;
-
- case 'V':
- return new Voc(ins);
-
- case 'R':
- type = VocRef;
- break;
-
- default:
- _error("Reading VocItem: unexpected type letter %c (0x%x)",c,c);
- }
-
- ins.ignore(1);
- expect_char(ins,",","after the type letter");
-
- char read_name[34];
- ins.get(read_name,sizeof(read_name),'='); // Read the name
- assert( ins.good() );
-
- expect_char(ins,"=","after the name");
- expect_char(ins," ","after the name");
-
- switch(type)
- {
- case Int:
- return new IntVocItem(read_name,ins);
-
- case Double:
- return new DoubleVocItem(read_name,ins);
-
- case String:
- return new StrVocItem(read_name,ins);
-
- case VocRef:
- {
- const char * voc_file_name = read_quoted_string(ins);
- if( voc_file_name[0] == '\0' )
- free((char *)voc_file_name), voc_file_name = 0;
- return new VocRefItem(read_name,voc_file_name);
- }
-
- default:
- _error("can't happen");
- }
- return 0; // as I said, it can't happen
- }
-
- // Read the value of the IntVocItem
- IntVocItem::IntVocItem(const char * _name, istream& ins)
- : GenericVocItem(_name,GenericVocItem::Int)
- {
- ins >> value;
- assert( ins.good() );
- }
-
- // Read the value of the Double Item
- DoubleVocItem::DoubleVocItem(const char * _name, istream& ins)
- : GenericVocItem(_name,GenericVocItem::Double)
- {
- ins >> value;
- assert( ins.good() );
- }
-
- // Read the value of the StrVocItem
- StrVocItem::StrVocItem(const char * _name, istream& ins)
- : GenericVocItem(_name,GenericVocItem::String),
- value(read_quoted_string(ins))
- {
- }
-
- //------------------------------------------------------------------------
- // Reading/Writing a vocabulary
-
- void Voc::write_down(ostream& outs) const
- {
- outs << "Voc(";
-
- if( check_option(ReadOnly) )
- outs << 'R';
-
- if( check_option(Homogeneous) )
- outs << 'H';
-
- outs << "): " << name << "= ";
- write_quoted_string(outs,comment);
- outs << " (\n";
-
- for( GenericVocItem * ip=first; ip != 0; ip=ip->next )
- (*ip).write_down(outs);
-
- outs << ")\n";
- assert( outs.good() );
- (*(Voc *)this).reset_option(Dirty); // Cast away const at 'this'
- }
-
- // Load a vocabulary from the istream
- Voc::Voc(istream& ins)
- : GenericVocItem("",GenericVocItem::Vocab),
- first(0), last(0), comment(0)
- {
- (int&)options = 0;
-
- {
- assert( ins.good() );
- char voc_string[4+1]; // Check for the "Voc(" string
- assert( ins.get(voc_string,sizeof(voc_string)).gcount() ==
- sizeof(voc_string)-1 );
- if( strcmp(voc_string,"Voc(") != 0 )
- _error("Wrong signature of the vocabulary '%s', 'Voc(' was expected",
- voc_string);
- }
-
- register int i;
- int should_be_read_only = 0;
- while( (i=expect_char(ins,"RH)","voc option char")) != 2 )
- if( i == 0 )
- should_be_read_only = 1;
- else if( i == 1 )
- set_option(Homogeneous);
- else
- _error("Can't happen");
-
- expect_char(ins,":","after reading voc options");
- expect_char(ins," ","after reading voc options");
-
- ins.get(name,sizeof(name),'=');
- expect_char(ins,"=","after reading voc name");
- expect_char(ins," ","after reading voc name");
-
- comment = read_quoted_string(ins);
-
- expect_char(ins," ","after reading voc comment");
- expect_char(ins,"(","after reading voc comment");
-
- for(;;) // Reading Voc items
- {
- assert( ins >> ws ); // it skips white spaces (incl \n)
- int c = ins.peek();
- if( c == ')' )
- break; // End-of-voc reached
- *this += *read_in(ins); // read an item and put in into the
- // vocabulary
- }
- expect_char(ins,")","after reading voc items");
- ins >> ws; // it skips white spaces (incl \n)
-
- if( should_be_read_only ) // After we've loaded all elements,
- set_option(ReadOnly); // set the read-only flag if necessary
- }
-
- // Load from the file (using VOCPATH if
- // necessary) and VocOfVoc_catalog
- // VOCPATH has the same form as PATH
- // and acts the same way
- Voc& Voc::load(const char * file_name)
- {
- const char * path_env_name = "VOCPATH";
-
- if( file_name == 0 )
- _error("Voc cannot be loaded: file name is empty");
-
- ifstream ins;
- if( file_name[0] == '/' ) // Absolute name was specified
- {
- ins.open(file_name);
- if( !ins.good() )
- perror("opening a file"),
- _error("Failed to open file '%s' to load a voc",file_name);
- }
- else // Try every path of VOCPATH in turn
- { // Assume "." (curr dir path) if
- char * path = getenv(path_env_name); // VOCPATH isn't set
- path = ( path != 0 && path[0] != '\0' ? strdup(path) : strdup(".") );
- assert( path != 0 );
- for(const char * curr_path = strtok(path,":"); curr_path != 0;
- curr_path = strtok(0,":"))
- {
- if( curr_path[0] == '\0' )
- continue;
-
- char full_file_name[300];
- sprintf(full_file_name, curr_path[strlen(curr_path)-1] == '/' ? "%s%s" :
- "%s/%s",curr_path,file_name);
- ins.open(full_file_name);
- if( ins.good() )
- break;
- }
- if( !ins.good() )
- _error("Failed to open file '%s' to load using VOCPATH '%s'",
- file_name,path);
- free(path);
- }
-
- assert( ins.good() );
- Voc * vp = new Voc(ins);
- (*vp).VocOfVoc_catalog();
- return *vp;
- }
-